using System;
using System.Linq;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Data;
using Nop.Core.Domain.ShareBuy;
using Nop.Core.Events;
using System.Collections.Generic;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Media;
using Nop.Core.Domain.Orders;
using System.Data;
using Nop.Data;
using Nop.Services.Events;

namespace Nop.Services.ShareBuy
{
    /// <summary>
    /// ShareBuy service
    /// </summary>
    public partial class ShareBuyService : IShareBuyService
    {
        #region Constants
        private const string SHAREBUY_BY_ID_KEY = "Nop.sharebuy.id-{0}";
        private const string SHAREBUY_PATTERN_KEY = "Nop.sharebuy.";
        #endregion

        #region Fields

        private readonly IRepository<ShareBuyItem> _shareBuyItemRepository;
        private readonly IRepository<ShareBuyCategory> _shareBuyCategoryRepository;
        private readonly IRepository<ShareBuyVote> _shareBuyVoteRepository;
        private readonly IRepository<ShareBuyPicture> _shareBuyPictureRepository;
        private readonly IRepository<Order> _orderRepository;
        private readonly IRepository<OrderItem> _orderItemRepository;
        private readonly IRepository<Picture> _pictureRepository;
        private readonly ICacheManager _cacheManager;
        private readonly IEventPublisher _eventPublisher;
        private readonly IDataProvider _dataProvider;
        private readonly IDbContextExtend _dbContext;

        #endregion

        #region Ctor

        public ShareBuyService (IRepository<ShareBuyItem> shareBuyItemRepository, 
            IRepository<ShareBuyVote> shareBuyVoteRepository, ICacheManager cacheManager,
            IEventPublisher eventPublisher, IRepository<ShareBuyPicture> shareBuyPictureRepository,
            IRepository<Picture> pictureRepository, IRepository<Order> orderRepository, IRepository<OrderItem> orderItemRepository, IDataProvider dataProvider, IDbContextExtend dbContext)
        {
            _shareBuyItemRepository = shareBuyItemRepository;
            _shareBuyVoteRepository = shareBuyVoteRepository;
            _shareBuyPictureRepository = shareBuyPictureRepository;
            _orderItemRepository = orderItemRepository;
            _orderRepository = orderRepository;
            _pictureRepository = pictureRepository;
            _cacheManager = cacheManager;
            _eventPublisher = eventPublisher;
            _dataProvider = dataProvider;
            _dbContext = dbContext;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Deletes a shareBuy
        /// </summary>
        /// <param name="shareBuyItem">ShareBuy item</param>
        public virtual void DeleteShareBuy(ShareBuyItem shareBuyItem)
        {
            if (shareBuyItem == null)
                throw new ArgumentNullException("shareBuyItem");

            _shareBuyItemRepository.Delete(shareBuyItem);

            _cacheManager.RemoveByPattern(SHAREBUY_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityDeleted(shareBuyItem);
        }

        /// <summary>
        /// Gets a shareBuy
        /// </summary>
        /// <param name="shareBuyId">The shareBuy identifier</param>
        /// <returns>ShareBuy</returns>
        public virtual ShareBuyItem GetShareBuyById(int shareBuyId)
        {
            if (shareBuyId == 0)
                return null;

            string key = string.Format(SHAREBUY_BY_ID_KEY, shareBuyId);
            //return _cacheManager.Get(key, () =>
            //{
                
            //});
            var n = _shareBuyItemRepository.GetById(shareBuyId);
            return n;
        }

        /// <summary>
        /// Gets all shareBuy
        /// </summary>
        /// <param name="categoryId">categoryId; null if you want to get all records</param>
        /// <param name="dateTo">keyword; null if you want to get all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>ShareBuy items</returns>
        public virtual IPagedList<ShareBuyItem> GetShareBuys (int categoryId,string keyword, int pageIndex, int pageSize, bool showHidden = false)
        {
            //var query = from a in _shareBuyCategoryRepository.Table
            //                join b in _shareBuyItemRepository.Table
            //                    on a.ShareBuyItemId equals b.Id into m
            //                from b in m.DefaultIfEmpty()
            //                select b;
            //if (!string.IsNullOrEmpty(keyword))
            //    query = query.Where(n => n.Title.Contains(keyword));
            //if (!showHidden)
            //    query = query.Where(n => !n.Deleted);
            //query = query.OrderByDescending(b => b.CreatedOnUtc);

            //prepare parameters
            var pCategoryId = _dataProvider.GetParameter();
            pCategoryId.ParameterName = "CategoryId";
            pCategoryId.Value = categoryId > 0 ? (object)categoryId : DBNull.Value;
            pCategoryId.DbType = DbType.Int32;

            var pKeywords = _dataProvider.GetParameter();
            pKeywords.ParameterName = "Keyword";
            pKeywords.Value = keyword != null ? (object)keyword : DBNull.Value;
            pKeywords.DbType = DbType.String;

            var pPageIndex = _dataProvider.GetParameter();
            pPageIndex.ParameterName = "PageIndex";
            pPageIndex.Value = pageIndex;
            pPageIndex.DbType = DbType.Int32;

            var pPageSize = _dataProvider.GetParameter();
            pPageSize.ParameterName = "PageSize";
            pPageSize.Value = pageSize;
            pPageSize.DbType = DbType.Int32;

            var pTotalRecord = _dataProvider.GetParameter();
            pTotalRecord.ParameterName = "TotalRecord";
            pTotalRecord.Direction = ParameterDirection.Output;
            pTotalRecord.DbType = DbType.Int32;

            //invoke stored procedure
            var shareBuys = _dbContext.ExecuteStoredProcedureList<ShareBuyItem>(
                "ShareBuyQuery",
                pCategoryId,
                pKeywords,
                pPageIndex,
                pPageSize,
                pTotalRecord);
            int totalRecords = (pTotalRecord.Value != DBNull.Value) ? Convert.ToInt32(pTotalRecord.Value) : 0;
            var shareBuy = new PagedList<ShareBuyItem>(shareBuys, pageIndex, pageSize, totalRecords);
            return shareBuy;
        }
        /// <summary>
        /// Gets the hot shareBuy
        /// </summary>
        /// <returns></returns>
        public virtual IPagedList<ShareBuyItem> GetHotShareBuys(int pageIndex, int pageSize, bool showHidden = false)
        {
            var queryGroup =from a in _shareBuyVoteRepository.Table
                                        group a by a.ShareBuyItemId into g
                                        select new { g.Key,Likes=g.Count()};

            var query = from a in _shareBuyItemRepository.Table
                         join b in queryGroup
                         on a.Id equals b.Key into m
                         from b in m.DefaultIfEmpty()
                         orderby b.Likes descending,a.Id descending
                         select a;
            if (!showHidden)
            {
                query = query.Where(n => !n.Deleted);
            }

            //var shareBuy = query.ToList();
            var shareBuy = new PagedList<ShareBuyItem>(query, pageIndex, pageSize);
            return shareBuy;
        }
        /// <summary>
        /// Inserts a shareBuy item
        /// </summary>
        /// <param name="shareBuy">ShareBuy item</param>
        public virtual void InsertShareBuy(ShareBuyItem shareBuy)
        {
            if (shareBuy == null)
                throw new ArgumentNullException("shareBuy");

            _shareBuyItemRepository.Insert(shareBuy);

            _cacheManager.RemoveByPattern(SHAREBUY_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityInserted(shareBuy);
        }

        /// <summary>
        /// Updates the shareBuy item
        /// </summary>
        /// <param name="shareBuy">ShareBuy item</param>
        public virtual void UpdateShareBuy(ShareBuyItem shareBuy)
        {
            if (shareBuy == null)
                throw new ArgumentNullException("shareBuy");

            _shareBuyItemRepository.Update(shareBuy);

            _cacheManager.RemoveByPattern(SHAREBUY_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityUpdated(shareBuy);
        }

        public virtual bool CheckShareBuyVoteExists(ShareBuyVote shareBuyVote)
        {
            var query = from a in _shareBuyVoteRepository.Table
                        .Where(n => n.CustomerId == shareBuyVote.CustomerId)
                        .Where(n=>n.ShareBuyItemId==shareBuyVote.ShareBuyItemId)
                        select a;
            bool isExsits = query.Count() > 0;
            return isExsits;
        }

        /// <summary>
        /// Insert a sharebuy vote record
        /// </summary>
        /// <param name="shareBuyVote"></param>
        public virtual void InsertShareBuyVote (ShareBuyVote shareBuyVote)
        {
            if (shareBuyVote.CustomerId <= 0 || shareBuyVote.ShareBuyItemId <= 0)
                throw new ArgumentNullException("shareBuy_vote");

            _shareBuyVoteRepository.Insert(shareBuyVote);

            //event notification
            _eventPublisher.EntityInserted(shareBuyVote);
        }

        /// <summary>
        /// Get a ShareBuyPicture
        /// </summary>
        /// <param name="shareBuyPictureId"></param>
        public virtual ShareBuyPicture GetShareBuyPictureById (int shareBuyPictureId)
        {
            if (shareBuyPictureId == 0)
                return null;

            var pp = _shareBuyPictureRepository.GetById(shareBuyPictureId);
            return pp;
        }

        /// <summary>
        /// Deletes a shareBuy picture
        /// </summary>
        /// <param name="ShareBuyPicture">ShareBuy picture</param>
        public virtual void DeleteShareBuyPicture (ShareBuyPicture shareBuyPicture)
        {
            if (shareBuyPicture == null)
                throw new ArgumentNullException("shareBuyPicture");

            _shareBuyPictureRepository.Delete(shareBuyPicture);

            //event notification
            _eventPublisher.EntityDeleted(shareBuyPicture);
        }

        /// <summary>
        /// Check customer can create share buy or not
        /// </summary>
        /// <param name="customerId"></param>
        /// <param name="orderId"></param>
        /// <param name="productId"></param>
        /// <returns></returns>
        public virtual bool CanCreateShareBuy(int customerId, int orderId, int? productId)
        {
            //order and productVariant is exists or not
            //var query = from a in _orderRepository.Table
            //            join b in _orderProductVariantRepository.Table
            //            on a.Id equals b.OrderId into m
            //            from b in m.DefaultIfEmpty()
            //            where a.CustomerId.Equals(customerId) && a.Id.Equals(orderId) && productVariantId==0?true:b.ProductVariantId.Equals(productVariantId)
            //            select b;
            ////sharebuy is exists or not
            //var query2 = from a in _shareBuyItemRepository.Table
            //             where a.CustomerId.Equals(customerId) && a.ProductVariantId.Equals(productVariantId)
            //             select a;

            //return query.Count() > 0 && query2.Count() == 0;

            //prepare parameters
            var pCustomerId = _dataProvider.GetParameter();
            pCustomerId.ParameterName = "CustomerId";
            pCustomerId.Value = customerId;
            pCustomerId.DbType = DbType.Int32;

            var pOrderId = _dataProvider.GetParameter();
            pOrderId.ParameterName = "OrderId";
            pOrderId.Value = orderId;
            pOrderId.DbType = DbType.Int32;

            var pProductId = _dataProvider.GetParameter();
            pProductId.ParameterName = "ProductId";
            pProductId.Value = productId != null ? (object)productId : DBNull.Value;
            pProductId.DbType = DbType.Int32;

            var pIsExists = _dataProvider.GetParameter();
            pIsExists.ParameterName = "IsExists";
            pIsExists.Direction = ParameterDirection.Output;
            pIsExists.DbType = DbType.Int32;

            //invoke stored procedure
            _dbContext.ExecuteStoredProcedure(
                "ShareBuyCheckCanShare",
                pCustomerId,
                pOrderId,
                pProductId,
                pIsExists);
            int isExists = (pIsExists.Value != DBNull.Value) ? Convert.ToInt32(pIsExists.Value) : 0;
            return isExists > 0;
        }

        /// <summary>
        /// Get can share order items
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        public IList<OrderItem> GetCanShareOrderItems(int orderId)
        {
            //prepare parameters
            var pOrderId = _dataProvider.GetParameter();
            pOrderId.ParameterName = "OrderId";
            pOrderId.Value = orderId > 0 ? (object)orderId : DBNull.Value;
            pOrderId.DbType = DbType.Int32;

            //invoke stored procedure
            var productVariants = _dbContext.ExecuteStoredProcedureList<OrderItem>(
                "CanShareOrderItemsQuery",
                pOrderId);
            return productVariants;
        }

        #endregion
    }
}
